﻿using System;
using System.Collections.Generic;
using System.Text;

namespace gsl_runtime_sdk
{
    public delegate string Input(string name, int id);
    public delegate void Output(string name, string value, int id);
    public delegate void Error(string error);
    public delegate string FunctionCall(string Function, string[] values);
    public delegate void Dispose(Compiler C);
    public class Compiler
    {
        List<string> PHPCode = new List<string>();
        List<string> JavaCode = new List<string>();
        public event FunctionCall OnOutsideFunction;
        string[] scpcode = new string[0] { };
        string[] scpcodeN = new string[0] { };
        public event Output OnNetworkSend;
        public event Input OnNetworkRecieve;
        public event Dispose OnDispose;
        public event Error OnError;
        public event Input OnRecieved;
        public event Output OnPrint;
        public List<Variable> Variable = new List<Variable>();
        public List<string> names = new List<string>();
        //public Dictionary<int, function> functions = new Dictionary<int, function>();
        private bool is_if = false;
        private bool is_else = false;
        private bool is_else_if = false;
        private bool in_if = false;
        public void ResetStorage()
        {
            scpcode = new string[0] { };
            scpcodeN = new string[0] { };
            Variable = new List<Variable>();
            names = new List<string>();
            //functions = new Dictionary<int, function>();
        }
        public int my_id = 0;
        int line = 0;
        public Compiler(int id)
        {
            my_id = id;
            line = 0;
        }

        public void Compile(string[] scode)
        {
            int total = scode.Length - 1;
            int at = 0;
            int i = 0;
            while (i == 0)
            {
                if (scode[at].Contains("exit();"))
                {
                    is_if = false;
                    is_else = false;
                    in_if = false;
                    is_else_if = false;
                    break;
                }

                Compile(scode[at]);

                if (at == total) break;
                at++;
            }
        }
        public void Compile(string scode)
        {
            if (scode == "") return;
            else
            {
                if (scode.Contains("\""))
                {
                    scpcode = scode.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    List<string> tempstr = new List<string>();
                    int last_q = 0;
                    int integer = 0;
                    string tempstr_com = "";
                    foreach (string str in scpcode)
                    {
                        if (str.Contains("\""))
                        {
                            if (last_q == 0)
                            {
                                tempstr_com += str.Replace("\"", "");
                                last_q = integer;
                            }
                            else if (last_q != 0)
                            {
                                tempstr_com += " " + str.Replace("\"", "");
                                tempstr.Add(tempstr_com);
                                last_q = 0;
                                tempstr_com = "";
                            }
                        }
                        else if (last_q != 0)
                        {
                            tempstr_com += " " + str;
                        }
                        else if (last_q == 0)
                        {
                            tempstr.Add(str);
                        }
                        integer++;
                    }
                    scpcode = tempstr.ToArray();
                    scpcodeN = tempstr.ToArray();
                }
                else
                {
                    scpcode = scode.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    scpcodeN = scode.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                }
                List<int> var_in_process = new List<int>();
                int i = 0;
                string compiled_analyzed_code = "";
                while (i < scpcode.Length)
                {

                    int index = names.IndexOf(scpcode[i]);
                    if (index >= 0)
                    {
                        scpcode[i] = Variable[index].Value;
                        var_in_process.Add(index);
                    }
                    compiled_analyzed_code += (i == 0 ? scpcode[i] : " " + scpcode[i]);

                    i++;
                }

                if (scpcodeN[0] == "if")
                {
                    switch (scpcodeN[2])
                    {
                        case "==":
                            PHPCode.Add("if($" + scpcodeN[1] + " == " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("if(" + scpcodeN[1] + " == " + scpcodeN[3] + ")\n{");
                            break;
                        case "<":
                            PHPCode.Add("if($" + scpcodeN[1] + " < " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("if(" + scpcodeN[1] + " < " + scpcodeN[3] + ")\n{");
                            break;
                        case "<=":
                            PHPCode.Add("if($" + scpcodeN[1] + " <= " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("if(" + scpcodeN[1] + " <= " + scpcodeN[3] + ")\n{");
                            break;
                        case ">":
                            PHPCode.Add("if($" + scpcodeN[1] + " > " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("if(" + scpcodeN[1] + " > " + scpcodeN[3] + ")\n{");
                            break;
                        case ">=":
                            PHPCode.Add("if($" + scpcodeN[1] + " >= " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("if(" + scpcodeN[1] + " >= " + scpcodeN[3] + ")\n{");
                            break;
                        default:
                            break;
                    }
                }
                else if (scpcodeN[0] == "else")
                {
                    PHPCode.Add("else\n{");
                    JavaCode.Add("else\n{");
                }
                else if (scpcodeN[0] == "elseif")
                {
                    switch (scpcodeN[2])
                    {
                        case "==":
                            PHPCode.Add("else if($" + scpcodeN[1] + " == " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("else if(" + scpcodeN[1] + " == " + scpcodeN[3] + ")\n{");
                            break;
                        case "<":
                            PHPCode.Add("else if($" + scpcodeN[1] + " < " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("else if(" + scpcodeN[1] + " < " + scpcodeN[3] + ")\n{");
                            break;
                        case "<=":
                            PHPCode.Add("else if($" + scpcodeN[1] + " <= " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("else if(" + scpcodeN[1] + " <= " + scpcodeN[3] + ")\n{");
                            break;
                        case ">":
                            PHPCode.Add("else if($" + scpcodeN[1] + " > " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("else if(" + scpcodeN[1] + " > " + scpcodeN[3] + ")\n{");
                            break;
                        case ">=":
                            PHPCode.Add("else if($" + scpcodeN[1] + " >= " + scpcodeN[3] + ")\n{");
                            JavaCode.Add("else if(" + scpcodeN[1] + " >= " + scpcodeN[3] + ")\n{");
                            break;
                        default:
                            break;
                    }
                }
                else if (scpcodeN[0] == "end")
                {
                    PHPCode.Add("\n}");
                    JavaCode.Add("\n}");
                }
                else if (in_if == true && is_if == false) return;
                else if (in_if == false && is_if == true) return;
                if (scpcode.Length > 1)
                {
                    switch (scpcodeN[1])
                    {
                        case "==":
                            PHPCode.Add("$" + scpcodeN[0] + " == " + scpcodeN[2]);
                            JavaCode.Add(scpcodeN[0] + " == " + scpcodeN[2]);
                            break;
                        case "=":
                            PHPCode.Add("$" + scpcodeN[0] + " = " + scpcodeN[2] + ";");
                            //if(scpcodeN
                            if (JavaCode.Contains("String " + scpcodeN[0] + ";"))
                            {
                                int location = JavaCode.IndexOf("String " + scpcodeN[0] + ";");
                                JavaCode.RemoveAt(location);

                                if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Bool) JavaCode.Insert(location, "boolean " + scpcodeN[0] + " = " + scpcodeN[2] + ";");
                                else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Double) JavaCode.Insert(location, "double " + scpcodeN[0] + " = " + scpcodeN[2] + ";");
                                else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Float) JavaCode.Insert(location, "float " + scpcodeN[0] + " = " + scpcodeN[2] + ";");
                                else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Integer) JavaCode.Insert(location, "int " + scpcodeN[0] + " = " + scpcodeN[2] + ";");
                                else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.String) JavaCode.Insert(location, "string " + scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            }
                            if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Bool) JavaCode.Add(scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Double) JavaCode.Add(scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Float) JavaCode.Add(scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.Integer) JavaCode.Add(scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            else if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.String) JavaCode.Add(scpcodeN[0] + " = \"" + scpcodeN[2] + "\";");
                            //else
                            //JavaCode.Add(scpcodeN[0] + " = " + scpcodeN[2] + ";");
                            break;
                        case "++":
                            PHPCode.Add("$" + scpcodeN[0] + " += 1;");
                            JavaCode.Add(scpcodeN[0] + " += 1;");
                            break;
                        case "--":
                            PHPCode.Add("$" + scpcodeN[0] + " -= 1;");
                            JavaCode.Add(scpcodeN[0] + " -= 1;");
                            break;
                        case "+":
                            if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.String)
                                PHPCode.Add("$" + scpcodeN[0] + " . " + scpcodeN[2] + ";");
                            else
                                PHPCode.Add("$" + scpcodeN[0] + " + " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " + " + scpcodeN[2] + ";");
                            break;
                        case "-":
                            PHPCode.Add("$" + scpcodeN[0] + " - " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " - " + scpcodeN[2] + ";");
                            break;
                        case "+=":
                            if (gsl_runtime_sdk.Variable.GetType(scpcodeN[0]) == gsl_runtime_sdk.Variable.var_type.String)
                                PHPCode.Add("$" + scpcodeN[0] + " .= " + scpcodeN[2] + ";");
                            else
                                PHPCode.Add("$" + scpcodeN[0] + " += " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " += " + scpcodeN[2] + ";");
                            break;
                        case "-=":
                            PHPCode.Add("$" + scpcodeN[0] + " -= " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " -= " + scpcodeN[2] + ";");
                            break;
                        case "*":
                            PHPCode.Add("$" + scpcodeN[0] + " *= " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " *= " + scpcodeN[2] + ";");
                            break;
                        case "/":
                            PHPCode.Add("$" + scpcodeN[0] + " / " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " / " + scpcodeN[2] + ";");
                            break;
                        case "*=":
                            PHPCode.Add("$" + scpcodeN[0] + " *= " + scpcodeN[2] + ";");
                            JavaCode.Add(scpcodeN[0] + " *= " + scpcodeN[2] + ";");
                            break;
                        default:
                            goto outofhere;
                    }
                outofhere:
                    if (scpcodeN[0] == "var")
                    {
                        if (names.Contains(scpcodeN[1]))
                        {

                        }
                        else if (scpcode.Length - 1 > 2)
                        {
                            lock (Variable)
                            {
                                lock (names)
                                {

                                    Variable temp_vars = new Variable(scpcodeN[3]);
                                    temp_vars.start = line;
                                    temp_vars.end = line;
                                    Variable.Add(temp_vars);
                                    names.Add(scpcodeN[1]);
                                    PHPCode.Add("$" + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    if (temp_vars.vtype == gsl_runtime_sdk.Variable.var_type.Bool) JavaCode.Add("boolean " + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    else if (temp_vars.vtype == gsl_runtime_sdk.Variable.var_type.Double) JavaCode.Add("double " + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    else if (temp_vars.vtype == gsl_runtime_sdk.Variable.var_type.Float) JavaCode.Add("float " + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    else if (temp_vars.vtype == gsl_runtime_sdk.Variable.var_type.Integer) JavaCode.Add("int " + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    else if (temp_vars.vtype == gsl_runtime_sdk.Variable.var_type.String) JavaCode.Add("String " + scpcodeN[1] + " = " + scpcodeN[3] + ";");
                                    
                                }
                            }
                        }
                        else
                        {
                            lock (Variable)
                            {
                                lock (names)
                                {
                                    Variable temp_vars = new Variable();
                                    temp_vars.Value = "null";
                                    temp_vars.start = line;
                                    temp_vars.end = line;
                                    Variable.Add(temp_vars);
                                    names.Add(scpcodeN[1]);
                                    PHPCode.Add("$" + scpcodeN[1] + " = " + "\"\";");
                                    JavaCode.Add("String " + scpcodeN[1] + ";");
                                }
                            }
                        }
                    }
                    else
                    {
                        if (scpcodeN[0] == "GSL.Console.Output")
                        {
                            if (scpcode.Length <= 1)
                                OnError("GSL.Console.Output is missing a argument!\nExample: GSL.Console.Output <var>");
                            else if (scpcode.Length - 1 >= 3)
                                OnPrint(scpcodeN[1], math_operator.Compile(new string[3] { scpcode[1], scpcodeN[2], scpcodeN[3] })[0].Value, my_id);
                            else
                                OnPrint(scpcodeN[1], scpcodeN[1], my_id);
                        }
                        else if (scpcodeN[0] == "GSL.Network.Output")
                        {
                            if (scpcode.Length <= 1)
                                OnError("GSL.Network.Output is missing a argument!\nExample: GSL.Network.Output <var>");
                            else if (scpcode.Length - 1 >= 3)
                                OnNetworkSend(scpcodeN[1], math_operator.Compile(new string[3] { scpcodeN[1], scpcodeN[2], scpcodeN[3] })[0].Value, my_id);
                            else
                                OnNetworkSend(scpcodeN[1], scpcodeN[1], my_id);
                        }
                        else if (scpcodeN[0] == "GSL.Network.All.Send")
                        {
                            if (scpcode.Length <= 1)
                                OnError("GSL.Network.All.Send is missing a argument!\nExample: GSL.Network.All.Send <from> <message>");
                            else
                                OnNetworkSend(scpcodeN[1], "From:" + scpcodeN[1] + ":Message:" + scpcodeN[2], my_id);
                        }
                        else if (scpcodeN[0] == "GSL.Network.Input")
                        {
                            Variable[var_in_process[0]].Value = OnNetworkRecieve(names[var_in_process[0]], my_id);
                        }
                        else if (scpcodeN[0] == "GSL.System.Sleep")
                        {
                            System.Threading.Thread.Sleep(int.Parse(scpcodeN[1]));
                        }
                        else if (scpcodeN[0] == "GSL.Dispose") OnDispose(this);
                        else if (scpcodeN[0] == "GSL.Console.Input")
                        {
                            Variable[var_in_process[0]].Value = OnRecieved(names[var_in_process[0]], my_id);
                            OnPrint(names[var_in_process[0]], scpcodeN[1], my_id);
                        }
                        else if (scpcodeN[0] == "GSL.String.Split")
                        {
                            if (scpcode.Length <= 2)
                                OnError("GSL.Network.Output is missing a argument!\nExample: GSL.String.Split <output> <split char/string> <position> <string>");
                            else
                                Variable[var_in_process[0]].Value = scpcodeN[4].Split(new string[1] { scpcodeN[2] }, StringSplitOptions.RemoveEmptyEntries)[int.Parse(scpcodeN[3])];
                        }
                        else if (scpcodeN[0] == "GSL.String.Split.Amount")
                        {
                            if (scpcode.Length <= 2)
                                OnError("GSL.Network.Output is missing a argument!\nExample: GSL.String.Split.Amount <splitcount> <split char/string> <string>");
                            else
                                Variable[var_in_process[0]].Value = "" + (scpcodeN[3].Split(new string[1] { scpcodeN[2] }, StringSplitOptions.RemoveEmptyEntries).Length - 1);
                        }
                        else
                        {
                            Variable Test = new Variable();
                            Test.Value = OnOutsideFunction(scpcodeN[0], scpcode);
                            if (Test.Value != "")
                                Variable[var_in_process[0]] = Test;
                        }
                    }
                }
                var_in_process.Clear();
                if (line == scode.Length - 1) return;
                line++;
            }
        }
        public void AddVariable(string val, string nme)
        {
            lock (Variable)
            {
                lock (names)
                {
                    Variable temp_vars = new Variable(val);
                    temp_vars.start = line;
                    temp_vars.end = line;
                    Variable.Add(temp_vars);
                    names.Add(nme);
                }
            }
        }
        public bool IsFunction(string code)
        {
            return true;
        }
    }
}
